/*-*-C-*-
 * General dialogue box processing for Windows CSE
 */

#include "resed.h"
#include "main.h"

#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"
#include "dbox.h"
#include "menu.h"

#include "format.h"
#include "windowedit.h"
#include "colours.h"
#include "gui.h"



/*
 * Checks to see whether a length field is exactly the right size for its
 *  corresponding string field.
 *
 * This is called when loading an object in order to determine the value to
 *  be stored in the corresponding "extension" record entry; this will be -1
 *  if an exact match is found (in which case the value is then displayed as
 *  "*" in the properties dbox), otherwise the length field is returned
 *  unchanged.
 *
 * Except that a length field which is too small - for example, after import
 *  of new messages - is updated to be the minimum length necessary.
 */

int gui_load_len_field (char *val, int *len)
{
    if (val == NULL && *len == 0 || *len == strlen(val) + 1)
        return -1;
    else
    if (val != NULL && *len < strlen(val) + 1)
    {
        *len = strlen(val) + 1;
        return -1;
    }
    else
        return *len;
}


/*
 * Returns the true value for a length field: "-1" is returned as the length
 *  of the associated string field, or as 0 if the string is NULL.
 */

int gui_save_len_field (char *val, int len)
{
    if (len == -1)
        return (val == NULL) ? 0 : strlen(val) + 1;
    else
        return len;
}


/*
 * Fill in the option, text and length fields in a dialogue box for a string
 * value which may be NULL.
 *
 * If the value is NULL, the option icon is deselected and the writable icon
 *  for the value is faded.
 */

error * gui_put_len_opt_str (
    WindowPtr dbox,       /* the dialogue box */
    int opticon,          /* the option icon */
    int valicon,          /* the writable icon to contain the text value */
    int lenicon,          /* the writable icon to contain the length value */
    char *val,            /* the text value - might be NULL */
    int len               /* the length value */
)
{
    Bool isnull = (val == NULL);

    dbox_setbutton (dbox, opticon, !isnull);
    dbox_shade (dbox, valicon, isnull);
    dbox_setstring (dbox, valicon, isnull ? "" : val);
    if (len == -1)
        dbox_setstring(dbox, lenicon, "*");
    else
        dbox_setint (dbox, lenicon, len);

    return NULL;
}


/*
 * Retrieve a string value and its length from option, text and length fields
 *  in a dialogue box.
 *
 * If the option icon is deselected, a NULL value is retrieved.
 *
 * If the option icon is selected, the length retrieved will be the greater
 *  of the value in the length field, or one more than the length of the
 *  string in the value field.
 *
 * Space for any new string value is malloc'd - and space occupied by any
 *  previous value is free'd.
 */

error * gui_get_len_opt_str (
    WindowPtr dbox,       /* the dialogue box */
    int opticon,          /* the option icon */
    int valicon,          /* the writable icon containing the text value */
    int lenicon,          /* the writable icon containing the length value */
    char **val,           /* to hold the retrieved text value - maybe NULL */
    int *len              /* to hold the retrieved length value */
)
{
    Bool isnull = !dbox_getbutton (dbox, opticon);
    char *ls = dbox_getstring(dbox, lenicon);
    int max = (*ls == '*') ? -1 : dbox_getint (dbox, lenicon);

    free (*val);

    if (isnull)
        *val = NULL;
    else
    {
        char *s = dbox_getstring (dbox, valicon);
        int n = strlen (s) + 1;

        if (max >= 0 && max < n)
            max = n;

        *val = malloc (n);
        if (*val == NULL)
            return error_lookup ("NoMem");
        strcpy (*val, s);
    }
    *len = max;

    return NULL;
}


/*
 * Fill in the option and text fields in a dialogue box for a string value
 *  which may be NULL, but which has no explicit length.
 *
 * If the value is NULL, the option icon is deselected and the writable icon
 *  for the value is faded.
 */

error * gui_put_opt_str (
    WindowPtr dbox,       /* the dialogue box */
    int opticon,          /* the option icon */
    int valicon,          /* the writable icon to contain the text value */
    char *val             /* the text value - might be NULL */
)
{
    Bool isnull = (val == NULL);

    dbox_setbutton (dbox, opticon, !isnull);
    dbox_shade (dbox, valicon, isnull);
    dbox_setstring (dbox, valicon, isnull ? "" : val);

    return NULL;
}


/*
 * Retrieve a string value from option and text fields in a dialogue box.
 *
 * If the option icon is deselected, a NULL value is retrieved.
 *
 * Space for any new string value is malloc'd - and space occupied by any
 *  previous value is free'd.
 */

error * gui_get_opt_str (
    WindowPtr dbox,       /* the dialogue box */
    int opticon,          /* the option icon */
    int valicon,          /* the writable icon containing the text value */
    char **val            /* to hold the retrieved text value - maybe NULL */
)
{
    Bool isnull = !dbox_getbutton (dbox, opticon);

    free (*val);

    if (isnull)
        *val = NULL;
    else
    {
        char *s = dbox_getstring (dbox, valicon);
        int n = strlen (s) + 1;

        *val = malloc (n);
        if (*val == NULL)
            return error_lookup ("NoMem");
        strcpy (*val, s);
    }

    return NULL;
}


/*
 * Fill in the text and length fields in a dialogue box for a mandatory
 *  string.
 *
 * A NULL value is interpreted as the empty string.
 */

error * gui_put_len_str (
    WindowPtr dbox,       /* the dialogue box */
    int valicon,          /* the writable icon to contain the text value */
    int lenicon,          /* the writable icon to contain the length value */
    char *val,            /* the text value - might be NULL */
    int len               /* the length value */
)
{
    dbox_setstring (dbox, valicon, (val == NULL) ? "" : val);

    if (len == -1)
        dbox_setstring(dbox, lenicon, "*");
    else
        dbox_setint (dbox, lenicon, len);

    return NULL;
}


/*
 * Retrieve a mandatory string value and its length from text and length
 *  fields in a dialogue box.
 *
 * If the value field contains the empty string, then a NULL value is
 *  retrieved.
 *
 * The length retrieved will be the greater of the value in the length field,
 *  or one more than the length of the string in the value field.
 *
 * Space for any new string value is malloc'd - and space occupied by any
 *  previous value is free'd.
 */

error * gui_get_len_str (
    WindowPtr dbox,       /* the dialogue box */
    int valicon,          /* the writable icon containing the text value */
    int lenicon,          /* the writable icon containing the length value */
    char **val,           /* to hold the retrieved text value - maybe NULL */
    int *len              /* to hold the retrieved length value */
)
{
    char *s = dbox_getstring (dbox, valicon);
    int n = strlen (s) + 1;
    char *ls = dbox_getstring(dbox, lenicon);
    int max = (*ls == '*') ? -1 : dbox_getint (dbox, lenicon);

    if (max >= 0 && max < n)
        max = n;

    free (*val);

    if (n == 1)       /* string is empty */
        *val = NULL;
    else
    {

        *val = malloc (n);
        if (*val == NULL)
            return error_lookup ("NoMem");
        strcpy (*val, s);
    }

    *len = max;

    return NULL;
}


/*
 * Fill in the text field in a dialogue box for a mandatory string value.
 *
 * If the value is NULL, the text field is filled with the empty string.
 */

error * gui_put_str (
    WindowPtr dbox,       /* the dialogue box */
    int valicon,          /* the writable icon to contain the text value */
    char *val             /* the text value - might be NULL */
)
{
    dbox_setstring (dbox, valicon, (val == NULL) ? "" : val);

    return NULL;
}


/*
 * Retrieve a mandatory string value from a text field in a dialogue box.
 *
 * If the value field contains the empty string, a NULL value is retrieved.
 *
 * Space for any new string value is malloc'd - and space occupied by any
 *  previous value is free'd.
 */

error * gui_get_str (
    WindowPtr dbox,       /* the dialogue box */
    int valicon,          /* the writable icon containing the text value */
    char **val            /* to hold the retrieved text value - maybe NULL */
)
{
    char *s = dbox_getstring (dbox, valicon);
    int n = strlen (s) + 1;

    free (*val);

    if (n == 1)   /* string is empty */
        *val = NULL;
    else
    {
        *val = malloc (n);
        if (*val == NULL)
            return error_lookup ("NoMem");
        strcpy (*val, s);
    }

    return NULL;
}


/*
 * Updates the value of the length field in a dialogue box, subject to it
 *  remaining greater than or equal to zero.
 */

error * gui_adjust_len (
    WindowPtr dbox,         /* the dialogue box */
    int lenicon,            /* the writable icon containing the length */
    int valicon,            /* ... and the corresponding string */
    int delta,              /* change required (usually +/-1) */
    unsigned int modifiers  /* if SHIFT bit set, multiply by 10 */
)
{
    char *s = (valicon < 0) ? "" : dbox_getstring (dbox, lenicon);
    int len = (*s == '*') ? strlen (dbox_getstring (dbox, valicon)) + 1 :
                            dbox_getint (dbox, lenicon);

    /* SHIFT-click => adjust by tens */
    if (modifiers & MODIFIER_SHIFT)
        delta *= 10;

    len += delta;
    if (len < 0)
        len = 0;

    return dbox_setint (dbox, lenicon, len);
}


/*
 * Updates the value of a coordinate field in a dialogue box.
 */

error * gui_adjust_coord (
    WindowPtr dbox,         /* the dialogue box */
    int icon,               /* the writable icon containing the coordinate */
    int delta,              /* increment or decrement amount (ie +/-4) */
    unsigned int modifiers  /* if SHIFT bit set, multiply delta by 10 */
)
{
    int x;

    /* SHIFT-click => adjust by ten times as much */
    if (modifiers & MODIFIER_SHIFT)
        delta *= 10;

    x = dbox_getint (dbox, icon) + delta;

    return dbox_setint (dbox, icon, x);
}


/*
 * Initialise the radio buttons and writable field for an optional event.
 *
 */

error * gui_put_opt_event (
    WindowPtr dbox,         /* the dialogue box */
    int dflticon,           /* the "raise default event" radio button */
    int othericon,          /* the "raise user-defined event" radio button */
    int noneicon,           /* the "do not raise any event" radio button */
    int valicon,            /* the writable icon containing the event */
    int val,                /* the event: 0 => default */
    Bool none               /* TRUE iff no event is to be raised */
)
{
    if (none)
    {
        dbox_setbutton (dbox, dflticon, FALSE);
        dbox_setbutton (dbox, noneicon, TRUE);
        dbox_setbutton (dbox, othericon, FALSE);
        dbox_shade (dbox, valicon, TRUE);
        dbox_setstring (dbox, valicon, "");
    }
    else
    {
        dbox_setbutton (dbox, dflticon, val == 0);
        dbox_setbutton (dbox, noneicon, FALSE);
        dbox_setbutton (dbox, othericon, val != 0);
        dbox_shade (dbox, valicon, val == 0);
        if (val == 0)
            dbox_setstring (dbox, valicon, "");
        else
            dbox_sethex (dbox, valicon, val);
    }

    return NULL;
}


/*
 * Retrieve details of an optional event from a dialogue box.
 */

error * gui_get_opt_event (
    WindowPtr dbox,
    int dflticon,
    int noneicon,
    int valicon,
    int *val,               /* event value stored here: 0 => default */
    unsigned *flags,        /* flags word to be updated */
    unsigned mask           /* identifies "no event" bit in flags */
)
{
    if (dbox_getbutton (dbox, noneicon))
        *flags &= ~mask;
    else
    {
        *flags |= mask;
        *val = dbox_getbutton (dbox, dflticon) ? 0 : 
                                 dbox_getint (dbox, valicon);
    }

    return NULL;
}


/*
 * Initialise the radio buttons and writable field for a mandatory event.
 */

error * gui_put_mand_event (
    WindowPtr dbox,         /* the dialogue box */
    int dflticon,           /* the "raise default event" radio button */
    int othericon,          /* the "raise user-defined event" radio button */
    int valicon,            /* the writable icon containing the event */
    int val                 /* the event: 0 => default */
)
{
    dbox_setbutton (dbox, dflticon, val == 0);
    dbox_setbutton (dbox, othericon, val != 0);
    dbox_shade (dbox, valicon, val == 0);
    if (val == 0)
        dbox_setstring (dbox, valicon, "");
    else
        dbox_sethex (dbox, valicon, val);

    return NULL;
}


/*
 * Retrieve details of a mandatory event from a dialogue box.
 */

error * gui_get_mand_event (
    WindowPtr dbox,
    int dflticon,
    int valicon,
    int *val
)
{
    if (dbox_getbutton (dbox, dflticon))
        *val = 0;
    else
        *val = dbox_getint (dbox, valicon);

    return NULL;
}


/*
 * Initialise the option icons and writables for a validation string.
 *
 * Note that NULL means "no validation string" and is distinct from the
 *  empty string.
 */

error * gui_put_allowable (
    WindowPtr dbox,         /* the dialogue box */
    int opticon,            /* the 'specify allowed chars' option icon */
    int lcicon,             /* the 'a-z' option icon */
    int ucicon,             /* the 'A-Z' option icon */
    int numicon,            /* the '0-9' option icon */
    int otheropticon,       /* the 'Other' option icon */
    int othervalicon,       /* the 'Other' validation string writable icon */
    int lenicon,            /* the 'length' writable icon */
    int upicon,             /* up adjuster associated with length */
    int downicon,           /* down adjuster associated with length */
    char *valid,            /* the validation string - may be NULL */
    int len                 /* and its maximum length */
)
{
    Bool hasvalid = valid != NULL;

    dbox_setbutton (dbox, opticon, hasvalid);
    dbox_shade (dbox, lcicon, !hasvalid);
    dbox_shade (dbox, ucicon, !hasvalid);
    dbox_shade (dbox, numicon, !hasvalid);
    dbox_shade (dbox, otheropticon, !hasvalid);

    /* set up validation string details */
    if (!hasvalid)      /* no validation string present */
    {
        dbox_setbutton (dbox, lcicon, FALSE);
        dbox_setbutton (dbox, ucicon, FALSE);
        dbox_setbutton (dbox, numicon, FALSE);
        dbox_setbutton (dbox, otheropticon, FALSE);
        dbox_setstring (dbox, othervalicon, NULL);
        dbox_shade (dbox, othervalicon, TRUE);
    }
    else                    /* validation string exists - possibly empty */
    {
        char *s;
        char *buf;

        buf = malloc (strlen (valid) + 1);
        if (buf == NULL)
            return error_lookup ("NoMem");

        strcpy (buf, valid);

        /* does the validation string contain "a-z"? */
        s = strstr (buf, "a-z");
        dbox_setbutton (dbox, lcicon, s != NULL);
        if (s != NULL)           /* if so, remove it */
        {
            *s = 0;
            strcat (buf, s+3);
        }

        /* does the validation string contain "A-Z"? */
        s = strstr (buf, "A-Z");
        dbox_setbutton (dbox, ucicon, s != NULL);
        if (s != NULL)           /* if so, remove it */
        {
            *s = 0;
            strcat (buf, s+3);
        }

        /* does the validation string contain "0-9"? */
        s = strstr (buf, "0-9");
        dbox_setbutton (dbox, numicon, s != NULL);
        if (s != NULL)           /* if so, remove it */
        {
            *s = 0;
            strcat (buf, s+3);
        }

        dbox_setstring (dbox, othervalicon, buf);

        /* shade icon and switch off other option unless something is left */
        dbox_setbutton (dbox, otheropticon, *buf != 0);
        dbox_shade (dbox, othervalicon, *buf == 0);

        free (buf);
    }

    /* set up length details */
    if (len == -1)
        dbox_setstring(dbox, lenicon, "*");
    else
        dbox_setint (dbox, lenicon, len);

    return NULL;
}


/*
 * Retrieve the validation string from a property dbox.
 *
 */

error * gui_get_allowable (
    WindowPtr dbox,         /* the dialogue box */
    int opticon,            /* the 'specify allowed chars' option icon */
    int lcicon,             /* the 'a-z' option icon */
    int ucicon,             /* the 'A-Z' option icon */
    int numicon,            /* the '0-9' option icon */
    int otheropticon,       /* the 'Other' option icon */
    int othervalicon,       /* the 'Other' validation string writable icon */
    int lenicon,            /* the 'length' writable icon */
    int upicon,             /* up adjuster associated with length */
    int downicon,           /* down adjuster associated with length */
    char **valid,           /* the location for the validation string */
    int *len                /* and for its maximum length */
)
{
    int minlen;

    if (!dbox_getbutton (dbox, opticon))    /* set to NULL */
    {
        *valid = NULL;
        minlen = 0;
    }
    else
    {
        Bool lc = dbox_getbutton (dbox, lcicon);
        Bool uc = dbox_getbutton (dbox, ucicon);
        Bool num = dbox_getbutton (dbox, numicon);
        Bool other = dbox_getbutton (dbox, otheropticon);
        char *otherval = dbox_getstring (dbox, othervalicon);

        /* calculate length for new validation string */
        minlen = 1;  /* for the terminator */
        if (lc) minlen += 3;
        if (uc) minlen += 3;
        if (num) minlen += 3;
        if (other) minlen += strlen (otherval);

        /* free any previous validation string */
        free (*valid);

        /* allocate space for the new one */
        *valid = malloc (minlen);
        if (*valid == NULL)
            return error_lookup ("NoMem");

        /* and create it */
        **valid = 0;
        if (lc) strcat (*valid, "a-z");
        if (uc) strcat (*valid, "A-Z");
        if (num) strcat (*valid, "0-9");
        if (other) strcat (*valid, otherval);
    }

    /* retrieve new maximum length */
    {
        char *ls = dbox_getstring(dbox, lenicon);
        int n = (*ls == '*') ? -1 : dbox_getint (dbox, lenicon);

        if (n >= 0 && n < minlen)
            n = minlen;

        *len = n;
    }

    return NULL;
}


/*
 * Initialise the option and writable icons describing a link from one
 *  gadget to another.
 */

error * gui_put_link (
    WindowPtr dbox,         /* the dialogue box */
    int opticon,            /* the option icon (link present or not) */
    int valicon,            /* the writable icon for the component id */
    int id                  /* and the value to put there (-1 => none) */
)
{
    Bool nolink = (id == -1);

    dbox_setbutton (dbox, opticon, !nolink);
    dbox_shade (dbox, valicon, nolink);

    if (nolink)
        dbox_setstring (dbox, valicon, NULL);
    else
        dbox_sethex (dbox, valicon, id);

    return NULL;
}


/*
 * Retrieve the component id that describes a link from one gadget to
 *  another; note that -1 means "no link".
 */

error * gui_get_link (
    WindowPtr dbox,         /* the dialogue box */
    int opticon,            /* the option icon (link present or not) */
    int valicon,            /* the writable icon for the component id */
    int *id                 /* and where to put the value (-1 for none) */
)
{
    if (dbox_getbutton (dbox, opticon))
        *id = dbox_getint (dbox, valicon);
    else
        *id = -1;

    return NULL;
}


/*
 * Returns integer between 0 and 15 inclusive or 255 according to the value
 * inside the display field 'icon' - which may be 0 - 15 or the string
 * 'transpval'.
 */

int gui_get_colour (WindowPtr dbox, int icon)
{
    char *s = dbox_getstring (dbox, icon);

    if (strstr (transpval, s) != NULL)    /* just in case it was truncated */
        return 255;

    return atoi (s);
}


/*
 * Set display field 'icon' in 'dbox' according to the value 'n' - which may
 *  be 0 - 16. The corresponding integer is displayed except for n = 16,
 *  which is represented by the value transpval.
 *
 * n = 255 is also interpreted as meaning "transparent".
 *
 * Appropriate foreground and background colours are also set for the icon.
 *
 * If 'realcolours' is TRUE, n may lie in the range 0 to 255; in this case,
 *  no attempt is made to display the correponding colour, and no attempt
 *  to interpret "transparent" is made.
 */

void gui_put_colour (WindowPtr dbox, int icon, int n, Bool realcolours)
{
    if (!realcolours && (n == 16 || n == 255))
    {
        dbox_setstring (dbox, icon, transpval);
        n = 0;
    }
    else
    {
        n &= realcolours ? 0xff : 0xf;
        dbox_setint (dbox, icon, n);
    }

    /* set icon colours if not real */
    if (!realcolours)
        dbox_iconflag (dbox, icon,
                IF_FIELD(FG, IF_FG_MASK) | IF_FIELD(BG, IF_BG_MASK),
                IF_FIELD(FG, uncolour[n]) | IF_FIELD(BG, n));

    return;
}


/*
 * Read the value representing an event from icons in a dialogue box:
 *
 *   If opticon is off, the value is zero.
 *   If opticon is on, the value is the contents of valicon.
 */

int gui_get_event (
    WindowPtr dbox,
    int opticon,
    int valicon
)
{
    if (dbox_getbutton (dbox, opticon))
        return dbox_getint (dbox, valicon);
    else
        return 0;
}


/*
 * Set the icons representing an event:
 *
 *   If n == 0, opticon is switched off, and valicon is faded.
 *   If n != 0, opticon is switched on, and valicon is set to n.
 */

void gui_put_event (
    WindowPtr dbox,
    int opticon,
    int valicon,
    int n
)
{
    Bool noevent = (n == 0);

    dbox_setbutton (dbox, opticon, !noevent);
    dbox_shade (dbox, valicon, noevent);
    if (noevent)
        dbox_setstring (dbox, valicon, "");
    else
        dbox_sethex (dbox, valicon, n);

    return;
}
